package com.sky.chapter1.waitAndnotify;


import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

/**
 * 多生产者、多消费者
 * @author : alanzhangyx
 * modifier : wushikai
 */
public class ProducerConsumer {

    //定义一个队列缓冲区，数据为Integer
    private static Queue<Integer> queue = new LinkedList<>();

    //设置缓冲区最大容量
    private static final int MAX_SIZE = 10;

    /**
     * 生产者
     *
     * <p>生产者进行V原语操作</p>
     * <ul>
     * <li>如果缓冲区没有达到MAX_SIZE，则生产一个产品（n个也行）放入缓冲区，并唤醒所有线程</li>
     * <li>否则使自己进入缓冲区的等待池</li>
     * </ul>
     *
     * @version  1.0.0
     * @author   alanzhangyx
     */
    class Producer implements Runnable{

        @Override
        public void run() {
            while (true) {
                synchronized (queue){ //锁住缓冲区, 防止 生产者和消费者拥挤进来;

                    if (queue.size() < MAX_SIZE) { //队列小于总容量则进行生产
                        int num = new Random().nextInt(100);
                        queue.offer(num);
                        queue.notifyAll();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("生产者" + Thread.currentThread().getName() + "生产了产品：" + num + "，此时缓冲区数据量为：" + queue.size());
                    } else {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 消费者。
     *
     * <p>消费者进行P原语操作</p>
     * <ul>
     * <li>如果缓冲区有数据，则从缓冲区取出一个产品（n个也行），并唤醒所有线程</li>
     * <li>否则使自己进入缓冲区的等待池</li>
     * </ul>
     *
     * @version  1.0.0
     * @author   alanzhangyx
     */
    class Consumer implements Runnable{

        @Override
        public void run() {
            while (true) {
                synchronized (queue){
                    if (queue.size() > 0) {   //队列有就进行消费
                        int num = queue.poll();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("消费者" + Thread.currentThread().getName() + "消费了产品：" + num + "，此时缓冲区数据量为：" + queue.size());
                        queue.notifyAll();
                    } else {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }


    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();

        //Thread构造函数需要一个Runnable对象即可构造一个新的线程，Runnable对象可以重复利用，不必new多个
        //一个消费者，一个生产者
        Consumer consumer = pc.new Consumer();
        Producer producer = pc.new Producer();

        //生产者和消费者谁先start都一样
        Thread thread = new Thread(consumer);
        thread.setName("消一哥");
        thread.start();

        Thread thread1 = new Thread(consumer);
        thread1.setName("潇洒哥");
        thread1.start();

        Thread thread2 = new Thread(producer);
        thread2.setName("卢哥");
        thread2.start();

        Thread thread3 = new Thread(producer);
        thread3.setName("卢克");
        thread3.start();

    }

}

